/**
 * Class: LNModel
 *		Base class for all models
 *		Typical usage is autogenerating models from database (and updating them when database changed) and
 *		adding extra methods for custom models.
 *
 *		Models can handle localized information, in such case (for example for 'name' column),
 *		Tarticles table contains (id,code)
 *		Tarticles_l10n table contains (id,fid_id,l10n_id,name)
 *		var item=this.site.models.Article.Get(1);
 *		item.name - contains CURRENT localization
 *		iiem.FetchLocales();
 *		item.l10ns[2].name - contains name of localization 2
 *
 *	
 *	// Add custom methods
 *	exports.add=[{
 *		_type:"model_methods",
 *		_config: {name:"BlogEntry"},
 *		calculate_statistics: function() {},
 *		"method1": function() {},
 *		"method2": function() {},
 *	}];
 *	
 *	// Create a blog entry
 *	var entry1=this.site.models.BlogEntry.Create({title:"Hello, world",abstract:"Some abstract",body:"Full text follows"});
 *	
 *	// Save entry
 *	entry1.SaveAll();
 *	
 *	// Get id of newly-saved entry
 *	var new_id=entry1.id;
 *	
 *	// Update title field in memory. data is not saved until calling Save(), only stored in model.
 *	entry1.title=entry1.title+" (updated)";
 *	
 *	// Save all updates to database
 *	entry1.SaveAll();
 *	
 *	// Gets a blog entry using a sql/_models/blog_entries/get.sql file
 *	var entry2=this.site.models.BlogEntry.Get(12);
 *	
 *	// Gets a blog entry using a sql/_models/blog_entries/alt_get_file.sql file
 *	var entry3=this.site.models.BlogEntry.Get("get_by_name",{name:"blog entry #40"});
 *	
 *	// Gets a list of blog entries using sql/_models/blog_entries/list_of_user.sql file
 *	var entries=this.site.models.BlogEntry.List("list_of_user",{user_id:10});
 *	
 **/

/**
 * Method: Constructor
 *		Called automatically. Never call it by yourself
 *
 * Parameters:
 *		sql typeof LNSql									- sql handle
 *		config typeof ModelConfig							- config of model
 *		config.name typeof String							- name of model, i.e. 'Article'
 *		config.table typeof String							- database table name, i.e. 'Tarticles'
 *		config.folder typeof String							- folder containing all autogenerated sql queries, i.e. 'articles'
 *		config.has_l10n typeof Boolean						- whether model has localization or not
 *		config.field_names typeof Array of String			- (for each field) database field names, real and emulated, i.e. for foreign key (if available), there is not only article_id but also 'article_name'
 *		config.field_datatypes typeof Array of String		- (for each field) datatypes. for emulated fields - empty string.
 *		config.field_fkmodels typeof Array of Object		- (for each field) foreign key information. if field is not a foreign key - empty string.
 *		config.field_names_l10n typeof Array of String		- (for each l10n field)
 *		config.field_datatypes_l10n typeof Array of String	- (for each l10n field)
 *		config.field_fkmodels_l10n typeof Array of String	- (for each l10n field)
 *		config.field_files typeof Array of String			- array of field names referencing to Tfiles table, i.e. ['image1_id', 'image2_id']
 *		config.methods typeof Object of Function			- methods (usually empty)
 */
function LNModel(sql,config)
{
	if (sql) this._sql=sql;
	if (config) this._config=config._config;
}

/**
 * Method: _SQL
 *		Returns sql handle
 *		Usage:
 *		exports.add=[{
 *			_type:"model_methods",
 *			_config: {name:"BlogEntry"},
 *			calculate_statistics: function() { this._SQL().execute(...); }
 *		}];
 *
 * Returns:
 *		typeof LNSql	- sql handle
 **/
LNModel.prototype._SQL=function()
{
	if (this._sql) return this._sql;
	return this.prototype._sql;
}

/**
 * Method: _Config
 *		Returns value of ModelConfig param, i.e. 'field_files' or something
 *
 * Parameters:
 *		f typeof String		- field name
 **/
LNModel.prototype._Config=function(f)
{
	if (this._config) return this._config[f];
	return this.prototype._config[f];
}

/**
 * Method: Init
 *		TODO(!) Dont know if it is used or not
 **/
LNModel.prototype.Init=function(obj)
{

}

/**
 * Method: Create
 *		Creates a new model from object.
 *		Generally used as a model prototype.
 *
 *		Usage (in controller):
 *		var item=this.site.models.Article.Create();
 *
 * Parameters:
 *		h typeof Object		- data for model contents, optional
 *
 * Returns:
 *		typeof LNModel		- new model
 **/
LNModel.prototype.Create=function(h)
{
	if (!h) h={};
	var obj=Object.create(this);
//	var ret=new LNModel();
//	ret.prototype=this;
	for (var k in h) obj[k]=h[k];
	obj.AfterNew();
	return obj;
}

/**
 * Method: RebuildFiles
 *		Rebuilds file datas, for fields given in field_files
 *		i.e. from field 'image_id' makes 'image_url'
 *
 **/
LNModel.prototype.RebuildFiles=function()
{
	var t=this;
	function tmp(conf) {
		var ff=t._Config(conf);
		for (var i=0;i<ff.length;i++) {
			var f=ff[i];
			if (!t[f+"_id"]) continue;
			t[f+"_url"]=t._SQL().fallback_href2.F("Files","file_folder_path",t[f+"_file_folder_id"])+t[f+"_id"]+"."+t[f+"_ext"];
		}
	}
	tmp("field_files");
	tmp("field_files_l10n");
}

/**
 * Method: CopyLocalesFromModel
 *		Copies locale fields from another model
 *
 *		Usage (in controller):
 *		var item1=this.site.models.Article.Get(1);
 *		var item2=this.site.models.Article.Get(2);
 *		item2.CopyLocalesFromModel(item1);
 *
 * Parameters:
 *		srcmodel typeof LNModel		- source model
 */
LNModel.prototype.CopyLocalesFromModel=function(srcmodel)
{
	if (!srcmodel.l10ns) srcmodel.FetchLocales();
	var l10ns=srcmodel.l10ns;
	var r={};
	for (var k in l10ns) {
		r[k]={};
		for (var k2 in l10ns[k]) if (k2!="id" && k2!="fid_id") r[k][k2]=l10ns[k][k2];
	}
	this.l10ns=r;
}

/**
 * Method: AfterNew
 *		Automatically called after .Create() method.
 *		Generally - makes init stuff - rebuilds files and such
 **/
LNModel.prototype.AfterNew=function()
{
	this.RebuildFiles();
	this.AfterNewCustom();
}

/**
 * Method: AfterNewCustom
 *		Empty.
 *		Override it for your model if needed.
 *		Will be called at the end of AfterNew
 *
 *
 **/
LNModel.prototype.AfterNewCustom=function()
{

}

/**
 * Method: AfterFetch
 *		Automatically called after .Get() or .List() methods.
 *		Generally - makes init stuff - rebuilds files and such
 **/
LNModel.prototype.AfterFetch=function()
{
	this.RebuildFiles();
	this.AfterFetchCustom();
}

/**
 * Method: AfterFetchCustom
 *		Empty.
 *		Override it for your model if needed.
 *		Will be called at the end of AfterFetch
 **/
LNModel.prototype.AfterFetchCustom=function()
{

}

/**
 * Method: Get
 *		Gets model from database
 *		Calls AfterFertch()
 *
 *		If no 'b' parameter given, get item from database with id=a
 *		If 'b' parameter is given, makes List(a,b)[0]
 *
 * Parameters:
 *		a typeof String or Integer	- (if no b is given), id in database table to fetch
 *		b typeof Object				- filter rules, runs and returns List(a,b)[0]
 *
 * Returns:
 *		typeof Model			- or undefined, if no data found
 */
LNModel.prototype.Get=function(a,b)
{
	if (b) return this.List(a,b)[0];
	var h=this._SQL().execute_and_fetch_one("_models/"+this._Config("folder")+"/get",{id:a});
	if (!h) return null;
	/*var obj=Object.create(this);
	for (var k in h) obj[k]=h[k];*/
	var obj=h;
	obj.__proto__=this;
	obj.AfterFetch();
	return obj;
}

/**
 * Method: GetCached
 *		Gets model from database
 *		Calls AfterFertch()
 *
 *		If no 'b' parameter given, get item from database with id=a
 *		If 'b' parameter is given, makes List(a,b)[0]
 *
 * Parameters:
 *		a typeof String or Integer	- (if no b is given), id in database table to fetch
 *		b typeof Object				- filter rules, runs and returns List(a,b)[0]
 *
 * Returns:
 *		typeof Model			- or undefined, if no data found
 */
LNModel.prototype.GetCached=function(a,b)
{
	if (b) throw "TODO";//return this.List(a,b)[0];
	var rr=this.GetRR();
	var folder=this._Config("folder");
	if (!rr._modelscache) rr._modelscache={};
	if (!rr._modelscache[folder]) rr._modelscache[folder]={};
	if (rr._modelscache[folder][a]) return rr._modelscache[folder][a];
	
	var h=rr.site.sql.execute_and_fetch_one("_models/"+folder+"/get",{id:a});
	if (!h) return null;
	/*var obj=Object.create(this);
	for (var k in h) obj[k]=h[k];*/
	var obj=h;
	obj.__proto__=this;
	obj.AfterFetch();
	rr._modelscache[folder][a]=obj;
	return obj;
}
/**
 * Method: FetchLocales
 *		Fetches all locale informations
 *		Modifies models such as:
 *		var item=this.site.models.Article.Get(1);
 *		item.name - contains CURRENT localization
 *		iiem.FetchLocales();
 *		item.l10ns[2].name - contains name of localization 2
 */
LNModel.prototype.FetchLocales=function()
{
//	if (!this.id) throw "Only work with instanced objects";
	var t=this;
	this.l10ns={};
	var arr=this._SQL().execute_and_fetch("_models/"+this._Config("folder")+"/get_all_locales",{id:this.id});
	for (var i=0,s=arr.length;i<s;i++) {
		l=arr[i];
		t.l10ns[l.l10n_id]=l;
	}
}

/**
 * Method: GetRR
 *		That's a (popular) hack to get LNRR object from-inside model
 *
 * Returns:
 *		typeof LNRR		- request-response object
 */
LNModel.prototype.GetRR=function()
{
	// I know it is a hack
	return this._SQL().fallback_href2;
}

/**
 * Method: FetchFileFolderFiles
 *		Goes through all fields searching for foreign keys to 'Tfile_folders' and fetches arrays of files
 *
 *		i.e. if there is in database:
 *			folder_id integer, foreign key (folder_id) references Tfile_folders
 *
 *		var item=this.site.models.Article.Get(1);
 *		item.FetchFileFolders();
 *
 *		item.folder contains array of File models, such as [{url:'/f/...',name:'...',id...}];
 */
LNModel.prototype.FetchFileFolderFiles=function()
{
	var rr=this.GetRR();
	var names=this._Config("field_names");
	var types=this._Config("field_datatypes");
	for (var i=0;i<names.length;i++) {
		if (types[i]!="file_folder") continue;
		if (!this[names[i]]) continue;
		this[names[i].replace(/_id$/,"")]=rr.site.models.File.List("list_of_file_folder",{file_folder_id:this[names[i]]});
	}
}

/**
 * Method: toJSONCustom
 *		Empty.
 *		Override it for your model if needed.
 *		called from toJSON method, used to add custom fields to JSON export.
 *		by default, toJSON method only exports fields mentioned in field_names and field_names_l10n
 *
 * Parameters:
 *		h typeof Object		- model representation, to which add fields
 */
LNModel.prototype.toJSONCustom=function(h)
{

}

/**
 * Method: toJSON
 *		Overrides which fields to export when JSON.stringify() is called.
 *		exports only fields mentioned in 'field_names' or 'field_names_l10n'
 *		after that, calls toJSONCustom
 *
 * Parameters:
 *
 * Returns:
 *		typeof Object		- model representation
 */
LNModel.prototype.toJSON=function()
{	
	var t=this;
	var h={id:this.id};
	function tmp(arr)
	{
		if (!arr) return;
		for (var i=0,s=arr.length;i<s;i++) h[arr[i]]=t[arr[i]];
	}
	tmp(this._Config("field_names"));
	tmp(this._Config("field_names_l10n"));
	if (this.l10ns) h.l10ns=this.l10ns;
	if (this.level) h.level=this.level;
	if (this.children) h.children=this.children;
	this.toJSONCustom(h);
	return h;
}

/**
 * Method: Apply
 *		Sets model fields to given in h object
 * Parameters:
 *		h typeof Ojbect		- object of fields to change, such as {code:'111',enabled:1,l10ns:{1:{name:'привет'},2:{name:'hello'}}}
 */
LNModel.prototype.Apply=function(h)
{
	for (var k in h) {
		if (k=="l10ns") {
			for (var l10n in h.l10ns) {
				if (!this.l10ns[l10n]) this.l10ns[l10n]={};
				for (var k2 in h.l10ns[l10n]) {
					this.l10ns[l10n][k2]=h.l10ns[l10n][k2];
				}
			}
			continue;
		}
		this[k]=h[k];
	}
}

/**
 * Method: ApplyAdminFields
 *		Used in autogenerated admin panels
 *		Applies fields given in HTTP post data
 *		Calls CheckFields
 */
LNModel.prototype.ApplyAdminFields=function(h)
{
	var t=this;
	var fn=t._Config("field_names");
	for (var i=0,s=fn.length;i<s;i++) {
		if (fn[i] in h) t[fn[i]]=h[fn[i]];
	}
	var fn=t._Config("field_names_l10n");
	for (var l10n in t.l10ns) {
		for (var i=0,s=fn.length;i<s;i++) {
			if (l10n+"__"+fn[i] in h) t.l10ns[l10n][fn[i]]=h[l10n+"__"+fn[i]];
		}
	}
	this.CheckFields(1);
}

/**
 * Method: CheckField
 *		Called by CheckFields
 *		(for now) for field datatype 'boolean', replaces null,undefined,'0' or '' to 0 and everything other to 1
 */
LNModel.prototype.CheckField=function(quiet,obj,name,datatype)
{
	switch (datatype) {
		case "boolean":
			if (!obj[name] || obj[name]=="0" || obj[name]=="") obj[name]=0; else obj[name]=1;
			break;
	}
}

/**
 * Method: CheckFields
 *		Checks all fields for data, possibly rewriting them
 *		Generally needed for replacing incorrect boolean field values with 1/0 so it could be correctly compared and placed to database
 */
LNModel.prototype.CheckFields=function(quiet)
{
	var t=this;
	var fn=t._Config("field_names");
	var ft=t._Config("field_datatypes");
	for (var i=0,s=fn.length;i<s;i++) this.CheckField(quiet,this,fn[i],ft[i]);
	var fn=t._Config("field_names_l10n");
	var ft=t._Config("field_datatypes_l10n");
	for (var l10n in t.l10ns) {
		for (var i=0,s=fn.length;i<s;i++) this.CheckField(quiet,this.l10ns[l10n],fn[i],ft[i])
	}
}


/**
 * Method: ListTree
 *		Returns list of models fetched hierarhicaly
 *		Each sublevel if placed in .children Array
 *
 * Parameters:
 *		sqlname typeof String		- sql name, optional, default 'list_level'
 *		href1 typeof Object			- filter values
 *		href2 typeof Object			- secondary filter values, not used
 *
 * Returns:
 *		typeof Array of LNModel
 */
LNModel.prototype.ListTree=function(sqlname,href1,href2)
{
	if (!href1) href1={};
	if (!sqlname) sqlname="list_level";
	var t=this;
	function tmp(parent_id,level)
	{
		href1.parent_id=parent_id;
		var arr=t.List(sqlname,href1,href2);
		for (var i=0,s=arr.length;i<s;i++) {
			var item=arr[i];
			item.level=level;
			item.children=tmp(item.id,level+1);
		}
		return arr;
	}
	return tmp(href1.parent_id,1);
}
/**
 * Method: ListHier
 *		Returns list of models fetched hierarhicaly
 *		All items are placed in same array, but with extra .level field set
 *		Also .level_leaf is true for leaf nodes and .level_root is true for roots
 *
 * Parameters:
 *		sqlname typeof String		- sql name, optional, default 'list_level'
 *		href1 typeof Object			- filter values
 *		href2 typeof Object			- secondary filter values, not used
 *
 * Returns:
 *		typeof Array of LNModel
 */
LNModel.prototype.ListHier=function(sqlname,href1,href2)
{
	var t=this;
	var ret=[];
	if (!href1) href1={};
	if (!sqlname) sqlname="with_recurse";
	if (sqlname.match(/^with_recurse/)) {
		ret=t.List(sqlname,href1,href2);
	} else {
		function tmp(parent_id,level)
		{
			href1.parent_id=parent_id;
			var arr=t.List(sqlname,href1,href2);
			for (var i=0,s=arr.length;i<s;i++) {
				var item=arr[i];
				ret.push(item);
				item.level=level;
				tmp(item.id,level+1);
			}
		}
		tmp(href1.parent_id,1);
	}
	var node;
	for (var i=0,s=ret.length;i<s;i++) {
		node = ret[i];
		node.level_root = node.level === 1;
		node.level_leaf = (i + 1 === s) || ret[i+1].level <= node.level;
	}
	return ret;
}

/**
 * Method: Count
 *		Returns count of items
 *
 * Parameters:
 *		sqlname typeof String		- sql name, optional, default 'count'
 *		href1 typeof Object			- filter values
 *		href2 typeof Object			- secondary filter values, not used
 *
 * Returns:
 *		typeof Integer				- count of items
 */
LNModel.prototype.Count=function(sqlname,href1,href2)
{
	if (!sqlname) sqlname="count";
	var ret=this._SQL().execute_and_fetch_one("_models/"+this._Config("folder")+"/"+sqlname,href1,href2);
	return ret.cnt;
}

/**
 * Method: ListFK
 *		Returns array of objects used for foreign key <select>'s, i.e. objects of {id:...,name:...}
 *
 * Returns:
 *		typeof Array of Object		- list of pairs {id:...,name:...}
 */
LNModel.prototype.ListFK=function()
{
	return this._SQL()[this._Config("has_hier")?"execute_and_fetch_h":"execute_and_fetch"]("_models/"+this._Config("folder")+"/list_fk",{},{});
	//return this._SQL()["execute_and_fetch"]("_models/"+this._Config("folder")+"/list_fk",{});
}

/**
 * Method: List
 *		Returns list of models
 *
 * Parameters:
 *		p1 typeof String			- sql name, optional, default 'list'
 *		p1 typeof Object			- {where:"",order_by:"",columns:"",from:""}
 *		href1 typeof Object			- filter values
 *		href2 typeof Object			- secondary filter values, not used
 *
 * Returns:
 *		typeof Array of LNModel
 */
LNModel.prototype.List=function(sqlname,href1,href2)
{
	if (!sqlname) sqlname="list";
	if (typeof sqlname === "object") {
		var sqlq=this._SQL().load("_models/"+this._Config("folder")+"/default");
		var dd=sqlq.match(/^([\s\S]+)\n(from\n[\s\S]*)$/);
		var sqlq=dd[1]+(sqlname.columns||"")+"\n"+dd[2]+" "+(sqlname.from||"")+"\n"+(sqlname.where?"where\n"+sqlname.where:"")+(sqlname.order_by?"\norder by "+sqlname.order_by:"");
		var ret=this._SQL().execute_and_fetch_single(sqlq,href1,href2);//"_models/"+this._Config("folder")+"/"+sqlname,href1,href2);
	} else {
		var ret=this._SQL().execute_and_fetch("_models/"+this._Config("folder")+"/"+sqlname,href1,href2);
	}
	for (var i=0,s=ret.length;i<s;i++) {
		/*var obj=Object.create(this);
		obj.Apply(ret[i]);
		obj.AfterFetch();
		ret[i]=obj;*/
		ret[i].__proto__=this;
		ret[i].AfterFetch();
	}
	return ret;
}

/**
 * Method: FromArray
 *		Generates list of LNModel from array of Object
 *
 * Parameters:
 *		arr typeof Array of Object		- source array
 *
 * Returns:
 *		typeof Array of LNModel			- array with objects casted to LNModel
 */
LNModel.prototype.FromArray=function(arr)
{
	for (var i=0,s=arr.length;i<s;i++) {
		/*var obj=Object.create(this);
		obj.Apply(arr[i]);
		obj.AfterFetch();
		ret.push(obj);*/
		arr[i].__proto__=this;
		arr[i].AfterFetch();
	}
	return arr;
}

/**
 * Method: Reorder
 *		Used for reordering models via admin panel
 *		Changes in-database column 'ordering'
 *
 * Parameters:
 *		ids typeof Array of Integer		- a new order of model
 */
LNModel.prototype.Reorder=function(ids)
{
//	throw ids;

	var orders=[];
	var nids=[];
	if (ids.constructor!=Array) throw new Error("typeof ids="+(typeof ids));
	for (var i=0;i<ids.length;i++) {
		if (!ids[i].match(/^\d+$/)) continue;
		nids.push(ids[i]);
		orders.push(this._SQL().execute_and_fetch_one("_models/"+this._Config("folder")+"/get_ordering",{id:ids[i]}).ordering);
	}
	orders.sort(function(a,b) { if (a<b) return -1; if (a>b) return 1; return 0; });

	//throw {nids:nids,orders:orders};	
	for (var i=0;i<nids.length;i++) {
		this._SQL().execute("_models/"+this._Config("folder")+"/set_ordering",{id:nids[i],ordering:-i-2});
	}
	for (var i=0;i<nids.length;i++) {
		this._SQL().execute("_models/"+this._Config("folder")+"/set_ordering",{id:nids[i],ordering:orders[i]});
	}
	
}

/**
 * Method: GetFieldInL10N
 *		Returns field in given l10n or fallback l10n
 *
 * Parameters:
 *		field typeof String		- field name
 *		l10n1 typeof Integer	- default l10n id
 *		l10n2 typeof Integer	- fallback l10n id
 *
 * Returns:
 *		typeof String			- value
 */
LNModel.prototype.GetFieldInL10N=function(field,l10n1,l10n2)
{
	if (!this.l10ns[l10n1]) return this.l10ns[l10n2][field];
	if (!this.l10ns[l10n2]) return this.l10ns[l10n1][field];
	return this.l10ns[l10n1][field] || this.l10ns[l10n2][field];
}

/**
 * Method: SaveAll
 *		Saves all data
 *		If locales information is inside model, saves locales also
 *
 *		call chain:
 *		CalcCustom()
 *		BeforeSaveCustom()
 *		... does it's work ...
 *		AfterSaveCustom()
 */
LNModel.prototype.SaveAll=function()
{
	var t=this;
	this.CalcCustom();
	this.BeforeSaveCustom();
	if (!this.id) {
		this._SQL().execute("_models/"+this._Config("folder")+"/insert",this);
		this.id=this._SQL().last_insert_id(this._Config("table"));
	} else {
		this._SQL().execute("_models/"+this._Config("folder")+"/update",this);
	}
	for (var l10n_id in this.l10ns) {
		var l=this.l10ns[l10n_id];
		if (!l.id) {
			this._SQL().execute("_models/"+this._Config("folder")+"/insert_locale",{fid_id:this.id},l);
			l.id=this._SQL().last_insert_id(this._Config("table")+"_l10n");
		} else {
			this._SQL().execute("_models/"+this._Config("folder")+"/update_locale",{fid_id:this.id},l);
		}
	}
	this.AfterSaveCustom();
}

/**
 * Method: BeforeSaveCustom
 *		Empty.
 *		Override it for your model if needed.
 *		called before SaveAll starts work.
 */
LNModel.prototype.BeforeSaveCustom=function()
{

}

/**
 * Method: AfterSaveCustom
 *		Empty.
 *		Override it for your model if needed.
 *		called after SaveAll ends work.
 */
LNModel.prototype.AfterSaveCustom=function()
{

}

/**
 * Method: BeforeDeleteCustom
 *		Empty.
 *		Override it for your model if needed.
 *		called before Delete starts work.
 */
LNModel.prototype.BeforeDeleteCustom=function()
{

}

/**
 * Method: AfterDeleteCustom
 *		Empty.
 *		Override it for your model if needed.
 *		called after Delete ends work.
 */
LNModel.prototype.AfterDeleteCustom=function()
{

}

/**
 * Method: CalcCustom
 *		Empty.
 *		Override it for your model if needed.
 *		called before SaveAll starts work.
 *
 *		Generally is used to update calc_* fields, such as calc_code, calc_fullname
 */
LNModel.prototype.CalcCustom=function()
{

}

/**
 * Method: Delete
 *		Deletes model from database
 *
 *		call chain:
 *		BeforeDeleteCustom()
 *		... does it's work ...
 *		AfterDeleteCustom()
 */
LNModel.prototype.Delete=function()
{
	this.BeforeDeleteCustom();
	if (this._Config("has_l10n")) {
		this._SQL().execute("_models/"+this._Config("folder")+"/delete_l10n",this);
	}
	this._SQL().execute("_models/"+this._Config("folder")+"/delete",this);
	this.AfterDeleteCustom();
}


exports.LNModel=LNModel;
